// index.ts
import axios from "axios";
import type {
  AxiosInstance,
  InternalAxiosRequestConfig,
  AxiosRequestConfig,
  AxiosResponse,
} from "axios";
import { errorNotify } from "@/utils/common";
import store from "@/store/store";

type Result<T> = {
  code: number;
  message: string;
  data: T;
};

// 导出Request类，可以用来自定义传递配置来创建实例
export class Request {
  // axios 实例
  instance: AxiosInstance;
  // 基础配置，url和超时时间
  baseConfig: AxiosRequestConfig = {
    baseURL: "/api",
    timeout: 10000,
    headers: {
      "Content-Type": "application/json;charset=UTF-8;",
    },
  };

  constructor(config: AxiosRequestConfig) {
    // 使用axios.create创建axios实例
    this.instance = axios.create(Object.assign(this.baseConfig, config));

    this.instance.interceptors.request.use(
      (config: InternalAxiosRequestConfig) => {
        // 一般会请求拦截里面加token，用于后端的验证
        const token = localStorage.getItem("access_token") as string;
        if (token) {
          // config.headers!.Authorization = token;
          config.headers.token = token;
        }

        // 如果是get或者delete请求，并且有params参数，将自动拼接url路径参数
        if (
          (config.method === "get" || config.method === "delete") &&
          config.params
        ) {
          let url = config.url + "?";

          for (const propName of Object.keys(config.params)) {
            const value = config.params[propName];

            let part = encodeURIComponent(propName) + "=";
            if (value !== null && typeof value !== "undefined") {
              if (value.constructor === Array) {
                value.forEach((item) => {
                  url += part + encodeURIComponent(item) + "&";
                });
              } else if (value.constructor === Object) {
                for (const key of Object.keys(value)) {
                  let params = propName + "[" + key + "]";
                  let subPart = encodeURIComponent(params) + "=";
                  url += subPart + encodeURIComponent(value[key]) + "&";
                }
              } else {
                url += part + encodeURIComponent(value) + "&";
              }
            }
          }
          url = url.slice(0, -1);
          config.params = {};
          config.url = url;
        }

        return config;
      },
      (err: any) => {
        // 请求错误，这里可以用全局提示框进行提示
        return Promise.reject(err);
      }
    );

    this.instance.interceptors.response.use(
      (res: AxiosResponse) => {
        // 直接返回res，当然你也可以只返回res.data
        // 系统如果有自定义code也可以在这里处理
        const { code, message } = res.data;
        if (code === 1) {
          errorNotify(message);
        } else if (code === 2) {
          store().setLoginBox(true);
        }
        return res;
      },
      (err: any) => {
        // 这里用来处理http常见错误，进行全局提示
        let message = "";
        switch (err.response.status) {
          case 400:
            message = "请求错误(400)";
            break;
          case 401:
            message = "未授权，请重新登录(401)";
            // 这里可以做清空storage并跳转到登录页的操作
            break;
          case 403:
            message = "拒绝访问(403)";
            break;
          case 404:
            message = "请求出错(404)";
            break;
          case 408:
            message = "请求超时(408)";
            break;
          case 500:
            message = "服务器错误(500)";
            break;
          case 501:
            message = "服务未实现(501)";
            break;
          case 502:
            message = "网络错误(502)";
            break;
          case 503:
            message = "服务不可用(503)";
            break;
          case 504:
            message = "网络超时(504)";
            break;
          case 505:
            message = "HTTP版本不受支持(505)";
            break;
          default:
            message = `连接出错(${err.response.status})!`;
        }
        // 这里错误消息可以使用全局弹框展示出来
        // 比如element plus 可以使用 ElMessage
        // ElMessage({
        //   showClose: true,
        //   message: `${message}，请检查网络或联系管理员！`,
        //   type: "error",
        // });
        errorNotify(message);
        // 这里是AxiosError类型，所以一般我们只reject我们需要的响应即可
        return Promise.reject(err.response);
      }
    );
  }

  // 定义请求方法
  public request = async <T = any>(
    config: AxiosRequestConfig
  ): Promise<Result<T>> => {
    const { data } = await this.instance.request(config);
    return new Promise<Result<T>>((resolve, reject) => {
      resolve(data);
    });
  };

  public get = <T = any>(url: string, params?: any): Promise<Result<T>> => {
    let config: AxiosRequestConfig = {
      url: url,
      method: "get",
    };
    if (params !== undefined) {
      config.params = params;
    }
    return this.request<T>(config);
  };

  public post = <T = any>(url: string, data?: any): Promise<Result<T>> => {
    let config: AxiosRequestConfig = {
      url: url,
      method: "post",
    };
    if (data !== undefined) {
      config.data = data;
    }
    return this.request<T>(config);
  };

  public put = <T = any>(url: string, data?: any): Promise<Result<T>> => {
    let config: AxiosRequestConfig = {
      url: url,
      method: "put",
    };
    if (data !== undefined) {
      config.data = data;
    }
    return this.request<T>(config);
  };

  public delete = <T = any>(url: string, params?: any): Promise<Result<T>> => {
    let config: AxiosRequestConfig = {
      url: url,
      method: "delete",
    };
    if (params !== undefined) {
      config.params = params;
    }
    return this.request<T>(config);
  };
}

// 默认导出Request实例
export default new Request({});
